package utils

import (
	"reflect"
)

// StructValuesEqual compares the field values of two structs deeply, excluding methods.
func StructValuesEqual(obj1, obj2 interface{}) bool {
	// Check if the inputs are of struct type
	v1 := reflect.ValueOf(obj1)
	v2 := reflect.ValueOf(obj2)

	// Ensure both are struct or pointers to struct
	if v1.Kind() == reflect.Ptr {
		v1 = v1.Elem()
	}
	if v2.Kind() == reflect.Ptr {
		v2 = v2.Elem()
	}
	if v1.Kind() != reflect.Struct || v2.Kind() != reflect.Struct {
		return false
	}

	// Ensure the two structs have the same type
	if v1.Type() != v2.Type() {
		return false
	}

	// Compare fields
	for i := 0; i < v1.NumField(); i++ {
		field1 := v1.Field(i)
		field2 := v2.Field(i)

		// Skip unexported fields
		if !field1.CanInterface() || !field2.CanInterface() {
			continue
		}

		// Compare field values
		if !deepEqual(field1, field2) {
			return false
		}
	}

	return true
}

// deepEqual handles the recursive comparison of field values.
func deepEqual(v1, v2 reflect.Value) bool {
	// Handle invalid values (e.g., nil values)
	if !v1.IsValid() && !v2.IsValid() {
		return true
	}
	if !v1.IsValid() || !v2.IsValid() {
		return false
	}

	// Handle comparable types directly
	switch v1.Kind() {
	case reflect.Bool, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
		reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
		reflect.Float32, reflect.Float64, reflect.String:
		return v1.Interface() == v2.Interface()

	case reflect.Ptr, reflect.Interface:
		// Recursively compare pointers or interfaces
		return deepEqual(v1.Elem(), v2.Elem())

	case reflect.Array, reflect.Slice:
		// Compare array or slice lengths
		if v1.Len() != v2.Len() {
			return false
		}
		// Compare each element
		for i := 0; i < v1.Len(); i++ {
			if !deepEqual(v1.Index(i), v2.Index(i)) {
				return false
			}
		}
		return true

	case reflect.Map:
		// Compare map lengths
		if v1.Len() != v2.Len() {
			return false
		}
		// Compare each key-value pair
		for _, key := range v1.MapKeys() {
			if !deepEqual(v1.MapIndex(key), v2.MapIndex(key)) {
				return false
			}
		}
		return true

	default:
		return reflect.DeepEqual(v1.Interface(), v2.Interface())
	}
}
